package blazingbubble.graphics.game;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.File;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;

import blazingbubble.config.Config;
import blazingbubble.game.*;
import spark.graphics.*;
import spark.graphics.scenegraph.SceneGraph;
import spark.utility.Clock;
import spark.exception.LoadingException;
import blazingbubble.game.command.GameCommandFactory;
import blazingbubble.graphics.game.renderer.GameRenderer;
import blazingbubble.exception.StartupException;
import org.apache.log4j.Logger;
import moltencore.game.Cannon;
import spark.utility.FrameBreak;

public class Scene implements GLEventListener, KeyListener, MouseMotionListener {
	private static Logger logger = Logger.getLogger(Scene.class);

	private Clock clock = new Clock();
	private blazingbubble.game.Game game;
	private GameCommandFactory commandFactory;

	private GameRenderer gameRenderer = new GameRenderer();
	private SceneGraph graph = new SceneGraph();
	
	private static Scene instance = new Scene();

	private Camera camera = new Camera();
	private float cameraSpeed = 10.00f;
	private float oldX = 0;
	private float oldY = 0;
	private boolean w = false;
	private boolean a = false;
	private boolean s = false;
	private boolean d = false;
	private boolean q = false;
	private boolean e = false;
	private boolean left = false;
	private boolean right = false;
	private boolean fire = false;
	private float passedMS = 0;
	private static final float FIRE_DELAY = 0.5f;
	private float fireTimer = FIRE_DELAY;
	private FrameBreak frameBreak;
	private Config config;

	public Scene(blazingbubble.game.Game game, GameCommandFactory commandFactory, Integer frameBreak) {
		this.commandFactory = commandFactory;
		this.game = game;
		this.frameBreak = new FrameBreak(frameBreak);
	}
	
	private Scene(){
	}

	@Override
	public void display(GLAutoDrawable drawable) {
		clock.update();
		frameBreak.breakFrame(clock.getElapsedMilliseconds());
		doFrame(clock.getElapsedSeconds());
	}

	private void doFrame(float elapsedSeconds) {
		fireTimer += elapsedSeconds;
		passedMS += elapsedSeconds;
		if(passedMS > 1000) {
			//System.out.println("FPS " + Clock.getInstance().getFramesPerSecond());
			passedMS = 0;
		}
		if(s) {
			camera.jump(-cameraSpeed * elapsedSeconds);
		}
		if(w) {
			camera.jump(cameraSpeed * elapsedSeconds);
		}
		if(d) {
			camera.strafe(-cameraSpeed * elapsedSeconds);
		}
		if(a) {
			camera.strafe(cameraSpeed * elapsedSeconds);
		}
		if(q) {
			camera.walk(-cameraSpeed * elapsedSeconds);
		}
		if(e) {
			camera.walk(cameraSpeed * elapsedSeconds);
		}
		if(fire) {
			if(fireTimer >= FIRE_DELAY) {
				commandFactory.getFire().fire();
				fireTimer = 0;
			}
		}
		if(left) {
		}
		if(right){
		}

		gameRenderer.prepareRender(graph);

		Renderer.getInstance().getGl().glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		Renderer.getInstance().getGl().glLoadIdentity();

		camera.update();
		graph.render();
	}

	@Override
	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
	}

	@Override
	public void init(GLAutoDrawable drawable) {
		Renderer.getInstance().initialize(drawable);
		Renderer.getInstance().setVSync(true);

		Renderer.getInstance().getGl().glEnable(GL.GL_TEXTURE_2D);
		Renderer.getInstance().getGl().glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
		Renderer.getInstance().getGl().glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);

		Renderer.getInstance().getGl().glEnable(GL.GL_BLEND);
		Renderer.getInstance().getGl().glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

		try {
			startup();
		}
		catch(StartupException e) {
			e.printStackTrace(); //TODO : this has to be moved somwehre else
		}

		camera.strafe(0);
		//camera.jump(-5);
		camera.walk(-21.66f);

		clock.reset();
	}

	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		Renderer.getInstance().resizeScenePerspectiveFov();
	}

	public void shutdown() {
	}

	@Override
	public void keyPressed(KeyEvent event) {
		if(event.getKeyCode() == KeyEvent.VK_S) {
			s = true;
		}
		if(event.getKeyCode() == KeyEvent.VK_W) {
			w = true;
		}
		if(event.getKeyCode() == KeyEvent.VK_A) {
			a = true;
		}
		if(event.getKeyCode() == KeyEvent.VK_D) {
			d = true;
		}
		if(event.getKeyCode() == KeyEvent.VK_Q) {
			q = true;
		}
		if(event.getKeyCode() == KeyEvent.VK_E) {
			e = true;
		}
		if(event.getKeyCode() == KeyEvent.VK_LEFT) {
			commandFactory.getCannonRotateStart().rotate(Cannon.Direction.Left);
			left = true;
		}
		if(event.getKeyCode() == KeyEvent.VK_RIGHT) {
			commandFactory.getCannonRotateStart().rotate(Cannon.Direction.Right);
			right = true;
		}
		if(event.getKeyCode() == KeyEvent.VK_UP) {
			fire = true;
		}
	}

	@Override
	public void keyReleased(KeyEvent event) {
		if(event.getKeyCode() == KeyEvent.VK_S) {
			s = false;
		}
		if(event.getKeyCode() == KeyEvent.VK_W) {
			w = false;
		}
		if(event.getKeyCode() == KeyEvent.VK_A) {
			a = false;
		}
		if(event.getKeyCode() == KeyEvent.VK_D) {
			d = false;
		}
		if(event.getKeyCode() == KeyEvent.VK_Q) {
			q = false;
		}
		if(event.getKeyCode() == KeyEvent.VK_E) {
			e = false;
		}
		if(event.getKeyCode() == KeyEvent.VK_LEFT) {
			left = false;
			commandFactory.getCannonRotateEnd().stopRotating();
		}
		if(event.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = false;
			commandFactory.getCannonRotateEnd().stopRotating();
		}
		if(event.getKeyCode() == KeyEvent.VK_UP) {
			fire = false;
		}
	}

	@Override
	public void keyTyped(KeyEvent event) {
	}

	@Override
	public void mouseDragged(MouseEvent arg0) {
	}

	@Override
	public void mouseMoved(MouseEvent event) {
		//float deltaX = event.getX() - oldX;
		//oldX = event.getX();
		//float deltaY = event.getY() - oldY;
		//oldY = event.getY();
		//camera.setYaw(camera.getYaw() - deltaX * 0.1f);
		//camera.setPitch(camera.getPitch() + deltaY * 0.1f);
	}

	private void startup() throws StartupException {
		gameRenderer.startup(game, config);
	}

	public void setConfig(Config config) {
		this.config = config;
	}

	public void setUp(blazingbubble.game.Game game, GameCommandFactory commandFactory, Integer frameBreak){
		this.game = game;
		this.commandFactory = commandFactory;
		this.frameBreak = new FrameBreak(frameBreak);
	}
	
	public static Scene getInstance(){
		return instance;
	}
}